﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SNSpirit.Service.Certificate;
using SNSpirit.Service.Navigation;
using SNSpirit.ViewModel.Authentication;

namespace SNSpirit.Service.Authentication.Integrated
{
    public class IntegratedNamePasswordAuthentication
    {
        public IntegratedNamePasswordAuthentication(NamePasswordAuthentication authentication, NamePasswordAuthenticationViewModel viewModel)
        {
            if (authentication == null)
                throw new ArgumentNullException("authentication");
            if (viewModel == null)
                throw new ArgumentNullException("viewModel");
            this.Authentication = authentication;
            this.ViewModel = viewModel;
        }

        public bool IsAuthenticating
        {
            get
            {
                return this._isAuthenticating == 1;
            }
        }

        public virtual AuthenticationToken Authenticate()
        {
            // check authenticate state
            if (this.Authentication.IsAuthenticated)
                return this.Authentication.Token;
            if (Interlocked.CompareExchange(ref this._isAuthenticating, 1, 0) == 0)
            {
                // not authenticated
                AuthenticationToken token = null;
                // navigate to view and wait for callback
                // hook
                this.ViewModel.StartLogin += new EventHandler(ViewModel_StartLogin);
                this.ViewModel.Cancelled += new EventHandler(ViewModel_Cancelled);
                // lock
                using (this._waitLock = new AutoResetEvent(false))
                {
                    // navigate
                    var session = NavigationService.Current.NavigateTo(ViewModel, NavigationMode.NewSession);
                    // wait
                    this._waitLock.WaitOne();
                    // close session
                    session.Close();
                    // token
                    token = this._receivedToken;
                    this._receivedToken = null;
                }
                // free
                this._waitLock = null;
                this.ViewModel.StartLogin -= new EventHandler(ViewModel_StartLogin);
                this.ViewModel.Cancelled -= new EventHandler(ViewModel_Cancelled);
                // return
                return token;
            }
            else
            {
                throw new InvalidOperationException("Another authenticate action is executing");
            }
        }

        #region protected

        protected NamePasswordAuthentication Authentication { get; set; }

        protected NamePasswordAuthenticationViewModel ViewModel { get; set; }

        #endregion protected

        #region private

        private int _isAuthenticating = 0;

        private AutoResetEvent _waitLock;

        private AuthenticationToken _receivedToken;

        private void ViewModel_Cancelled(object sender, EventArgs e)
        {
            this._receivedToken = null;
            this._waitLock.Set();
        }

        private void ViewModel_StartLogin(object sender, EventArgs e)
        {
            var certificate = this.ViewModel.GetCertificate();
            if (certificate == null)
                throw new InvalidOperationException("Invalid certificate");
            var result = this.Authentication.Authenticate(certificate);
            if (result.Error == null && result.Token != null && !result.Token.IsAnonymous)
            {
                // succeed
                this._receivedToken = result.Token;
                this._waitLock.Set();
            }
            else
            {
                // failed
                this.ViewModel.NotifyCertificateError();
            }
        }

        #endregion private
    }
}